package com.capgemini.cn.authority.core.security;

import com.capgemini.cn.authority.data.entity.Authorities;
import com.capgemini.cn.authority.data.entity.GroupMembers;
import com.capgemini.cn.authority.data.entity.Groups;
import com.capgemini.cn.authority.data.entity.Roles;
import com.capgemini.cn.authority.data.repository.GroupMembersRepository;
import lombok.Getter;
import lombok.Setter;
import org.pac4j.core.authorization.generator.AuthorizationGenerator;
import org.pac4j.core.context.WebContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Created at 2017/8/30
 *
 * @author Andriy
 */
@Component(value = SecurityAuthorizationGenerator.COMPONENT_NAME)
public class SecurityAuthorizationGenerator implements AuthorizationGenerator<SecurityProfile> {

    public static final String COMPONENT_NAME = "securityAuthorizationGenerator";
    private static final Logger LOGGER = LoggerFactory.getLogger(SecurityAuthorizationGenerator.class);

    @Getter
    @Setter
    @Autowired
    private GroupMembersRepository groupMembersRepository;

    @Transactional(readOnly = true)
    @Override
    public SecurityProfile generate(WebContext context, SecurityProfile profile) {
        Assert.notNull(profile, "参数SecurityProfile为Null，不能正常处理授权业务！");
        Assert.notNull(profile.getUsername(), "参数SecurityProfile中的username属性值为空，不能正常处理授权业务！");
        LOGGER.debug("开始处理用户{}的授权业务逻辑。", profile.getUsername());
        // 查询用户的角色和权限
        GroupMembers groupMembers = this.groupMembersRepository.findByUserName(profile.getUsername());
        if (!ObjectUtils.isEmpty(groupMembers)) {
            Groups groups = groupMembers.getGroups();
            Assert.notNull(groups, "无法根据用户名为" + groupMembers.getUserName() + "的数据查询到对应的用户组信息，该条数据异常请检查用户组成员信息的有效性和完整性！");
            LOGGER.debug("查询到用户{}被分配在{}用户组中。", profile.getUsername(), groups.getGroupName());
            // 获取用户组及其父级所有的角色信息
            List<Roles> rolesList = this.getAllRoles(groups);
            // 处理用户对应的角色和权限信息
            if (!ObjectUtils.isEmpty(rolesList)) {
                // 角色信息列表，仅用作日志打印
                List<String> roleInfoList = new ArrayList<>();
                // 权限信息列表，仅用作日志打印
                List<String> authorityInfoList = new ArrayList<>();
                // 授权用户角色信息
                for (Roles role : rolesList) {
                    profile.addRole(String.valueOf(role.getId()));
                    roleInfoList.add(role.getRoleName());
                    LOGGER.debug("为用户{}分配角色：{}...", groupMembers.getUserName(), role.getRoleName());
                    List<Authorities> authoritiesList = role.getAuthorities();
                    if (!ObjectUtils.isEmpty(authoritiesList)) {
                        // 授权用户权限信息
                        for (Authorities authority : authoritiesList) {
                            profile.addPermission(authority.getAuthorityCode());
                            authorityInfoList.add(authority.getAuthorityName());
                            LOGGER.debug("为用户{}分配角色{}包含的权限：【权限名】-{}、【权限编码】-{}、【权限类型】-{}", groupMembers.getUserName(), role.getRoleName(), authority.getAuthorityName(), authority.getAuthorityCode(), authority.getAuthoritiesType().getTypeName());
                        }
                    } else {
                        LOGGER.warn("用户名{}所拥有的角色{}未被分配任何权限！", groupMembers.getUserName(), role.getRoleName());
                    }
                }
                LOGGER.info("用户{}被分配{}个角色：{}，一共包含{}个权限：{}", groupMembers.getUserName(), roleInfoList.size(), Arrays.toString(roleInfoList.toArray()), authorityInfoList.size(), Arrays.toString(authorityInfoList.toArray()));
            } else {
                LOGGER.warn("用户名{}所在的用户组{}未被分配任何角色！", groupMembers.getUserName(), groups.getGroupName());
            }
        } else {
            LOGGER.warn("无法根据用户名{}查询到对应的分组信息，故而无法正常处理授权业务。请确保该用户已经被合理分配至用户组！", profile.getUsername());
        }
        // TODO 授权成功后可以把当前用户相关的权限信息放到缓存中，以便后续在CheckRoleAndPermissionAuthorizer.isAuthorized()函数中判断授权行为时提高性能
        return profile;
    }


    /**
     * 通过迭代获取用户组及其父级的所有角色信息
     *
     * @param groups 用户所属分组
     * @return 返回指定用户组所有的角色信息
     */
    private List<Roles> getAllRoles(Groups groups) {
        List<Roles> resultList = new ArrayList<>();
        // 首先把用户组自身的权限获取出来
        resultList.addAll(groups.getRoles());
        // 然后判断是否存在父用户组，如果存在则采用递归的方式获取其拥有的角色信息
        if (!ObjectUtils.isEmpty(groups.getParentGroup())) {
            resultList.addAll(this.getAllRoles(groups.getParentGroup()));
        }
        return resultList;
    }
}
